﻿namespace Vanara.PInvoke;

public static partial class Kernel32
{
	/// <summary>Adds a dynamic function table to the dynamic function table list.</summary>
	/// <param name="FunctionTable">
	/// A pointer to an array of function entries. For a definition of the <c>PRUNTIME_FUNCTION</c> type, see rtlsupportapi.h. For more
	/// information on runtime function entries, see the calling convention documentation for the processor.
	/// </param>
	/// <param name="EntryCount">The number of entries in the FunctionTable array.</param>
	/// <param name="BaseAddress">
	/// The base address to use when computing full virtual addresses from relative virtual addresses of function table entries.
	/// </param>
	/// <returns>If the function succeeds, the return value is <c>TRUE</c>. Otherwise, the return value is <c>FALSE</c>.</returns>
	/// <remarks>
	/// <para>
	/// Function tables are used on 64-bit Windows to determine how to unwind or walk the stack. These tables are usually generated by
	/// the compiler and stored as part of the image. However, applications must provide the function table for dynamically generated
	/// code. For more information about function tables, see the architecture guide for your system.
	/// </para>
	/// <para>
	/// This function is useful for code that is generated from a template or generated only once during the life of the process. For
	/// more dynamically generated code, use the RtlInstallFunctionTableCallback function.
	/// </para>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtladdfunctiontable NTSYSAPI BOOLEAN RtlAddFunctionTable(
	// PRUNTIME_FUNCTION FunctionTable, DWORD EntryCount, DWORD64 BaseAddress );
	[DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)]
	[PInvokeData("rtlsupportapi.h", MSDNShortId = "4717f29e-c5f8-4b02-a7c8-edd065f1c793")]
	[return: MarshalAs(UnmanagedType.U1)]
	public static extern bool RtlAddFunctionTable([In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] IMAGE_RUNTIME_FUNCTION_ENTRY[] FunctionTable, uint EntryCount, ulong BaseAddress);

	/// <summary>Retrieves a context record in the context of the caller.</summary>
	/// <param name="ContextRecord">A pointer to a CONTEXT structure.</param>
	/// <returns>This function does not return a value.</returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtlcapturecontext NTSYSAPI VOID RtlCaptureContext( PCONTEXT
	// ContextRecord );
	[DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)]
	[PInvokeData("rtlsupportapi.h", MSDNShortId = "e2ce0cde-43ab-4681-be66-bd7509fd6ca2")]
	public static extern void RtlCaptureContext(ref CONTEXT ContextRecord);

	/// <summary>Retrieves a context record in the context of the caller.</summary>
	/// <param name="contextFlags">
	/// The context flags to specify what aspects of the context to capture. See <see cref="CONTEXT_FLAG"/> for pre-configured values.
	/// </param>
	/// <returns>The captured <c>CONTEXT</c> structure.</returns>
	[PInvokeData("rtlsupportapi.h", MSDNShortId = "e2ce0cde-43ab-4681-be66-bd7509fd6ca2")]
	[return: AddAsCtor]
	public static CONTEXT RtlCaptureContext(uint contextFlags = uint.MaxValue)
	{
		if (contextFlags == uint.MaxValue) contextFlags = CONTEXT_FLAG.CONTEXT_ALL;
		var ctx = new CONTEXT { ContextFlags = contextFlags };
		RtlCaptureContext(ref ctx);
		return ctx;
	}

	/// <summary>Removes a dynamic function table from the dynamic function table list.</summary>
	/// <param name="FunctionTable">
	/// A pointer to an array of function entries that were previously passed to RtlAddFunctionTable or an identifier previously passed
	/// to RtlInstallFunctionTableCallback. For a definition of the <c>PRUNTIME_FUNCTION</c> type, see rtlsupportapi.h.
	/// </param>
	/// <returns>If the function succeeds, the return value is <c>TRUE</c>. Otherwise, the return value is <c>FALSE</c>.</returns>
	/// <remarks>
	/// Function tables are used on 64-bit Windows to determine how to unwind or walk the stack. These tables are usually generated by
	/// the compiler and stored as part of the image. However, applications must provide the function table for dynamically generated
	/// code. For more information about function tables, see the architecture guide for your system.
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtldeletefunctiontable NTSYSAPI BOOLEAN RtlDeleteFunctionTable(
	// PRUNTIME_FUNCTION FunctionTable );
	[DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)]
	[PInvokeData("rtlsupportapi.h", MSDNShortId = "42bc3d83-8053-40e9-b153-f68733d0cb2b")]
	[return: MarshalAs(UnmanagedType.U1)]
	public static extern bool RtlDeleteFunctionTable([In, MarshalAs(UnmanagedType.LPArray)] IMAGE_RUNTIME_FUNCTION_ENTRY[] FunctionTable);

	/// <summary>Removes a dynamic function table from the dynamic function table list.</summary>
	/// <param name="FunctionTable">
	/// A pointer to an array of function entries that were previously passed to RtlAddFunctionTable or an identifier previously passed
	/// to RtlInstallFunctionTableCallback. For a definition of the <c>PRUNTIME_FUNCTION</c> type, see rtlsupportapi.h.
	/// </param>
	/// <returns>If the function succeeds, the return value is <c>TRUE</c>. Otherwise, the return value is <c>FALSE</c>.</returns>
	/// <remarks>
	/// Function tables are used on 64-bit Windows to determine how to unwind or walk the stack. These tables are usually generated by
	/// the compiler and stored as part of the image. However, applications must provide the function table for dynamically generated
	/// code. For more information about function tables, see the architecture guide for your system.
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtldeletefunctiontable NTSYSAPI BOOLEAN RtlDeleteFunctionTable(
	// PRUNTIME_FUNCTION FunctionTable );
	[DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)]
	[PInvokeData("rtlsupportapi.h", MSDNShortId = "42bc3d83-8053-40e9-b153-f68733d0cb2b")]
	[return: MarshalAs(UnmanagedType.U1)]
	public static extern bool RtlDeleteFunctionTable([In] ulong FunctionTable);

	/// <summary>Adds a dynamic function table to the dynamic function table list.</summary>
	/// <param name="TableIdentifier">
	/// The identifier for the dynamic function table callback. The two low-order bits must be set. For example, BaseAddress|0x3.
	/// </param>
	/// <param name="BaseAddress">The base address of the region of memory managed by the callback function.</param>
	/// <param name="Length">The size of the region of memory managed by the callback function, in bytes.</param>
	/// <param name="Callback">
	/// A pointer to the callback function that is called to retrieve the function table entries for the functions in the specified
	/// region of memory. For a definition of the <c>PGET_RUNTIME_FUNCTION_CALLBACK</c> type, see rtlsupportapi.h.
	/// </param>
	/// <param name="Context">A pointer to the user-defined data to be passed to the callback function.</param>
	/// <param name="OutOfProcessCallbackDll">
	/// <para>
	/// An optional pointer to a string that specifies the path of a DLL that provides function table entries that are outside the process.
	/// </para>
	/// <para>
	/// When a debugger unwinds to a function in the range of addresses managed by the callback function, it loads this DLL and calls the
	/// <c>OUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK_EXPORT_NAME</c> function, whose type is <c>POUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK</c>.
	/// For more information, see the definitions of these items in rtlsupportapi.h.
	/// </para>
	/// </param>
	/// <returns>If the function succeeds, the return value is <c>TRUE</c>. If the function fails, the return value is <c>FALSE</c>.</returns>
	/// <remarks>
	/// <para>
	/// Function tables are used on 64-bit Windows to determine how to unwind or walk the stack. These tables are usually generated by
	/// the compiler and stored as part of the image. However, applications must provide the function table for dynamically generated
	/// code. For more information about function tables, see the architecture guide for your system.
	/// </para>
	/// <para>
	/// This function is useful for very dynamic code. The application specifies the memory range for the generated code, but does not
	/// need to generate a table until it is needed by an unwind request. At that time, the system calls the callback function with the
	/// Context and the control address. The callback function must return the runtime function entry for the specified address. Be sure
	/// to avoid creating a deadlock between the callback function and the code generator.
	/// </para>
	/// <para>
	/// For code that is generated from a template or generated only once during the life of the process, use the RtlAddFunctionTable function.
	/// </para>
	/// </remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtlinstallfunctiontablecallback NTSYSAPI BOOLEAN
	// RtlInstallFunctionTableCallback( DWORD64 TableIdentifier, DWORD64 BaseAddress, DWORD Length, PGET_RUNTIME_FUNCTION_CALLBACK
	// Callback, PVOID Context, PCWSTR OutOfProcessCallbackDll );
	[DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)]
	[PInvokeData("rtlsupportapi.h", MSDNShortId = "63b35b17-0b0e-46ed-9dbf-98290ab08bd1")]
	[return: MarshalAs(UnmanagedType.U1)]
	public static extern bool RtlInstallFunctionTableCallback(ulong TableIdentifier, ulong BaseAddress, uint Length, GetRuntimeFunctionCallback Callback,
		IntPtr Context = default, [MarshalAs(UnmanagedType.LPWStr)] string? OutOfProcessCallbackDll = null);

	/// <summary>Searches the active function tables for an entry that corresponds to the specified PC value.</summary>
	/// <param name="ControlPc">The virtual address of an instruction bundle within the function.</param>
	/// <param name="ImageBase">The base address of module to which the function belongs.</param>
	/// <param name="HistoryTable">
	/// <para>The global pointer value of the module.</para>
	/// <para>This parameter has a different declaration on x64 and ARM systems. For more information, see x64 Definition and ARM Definition.</para>
	/// </param>
	/// <returns>
	/// If there is no entry in the function table for the specified PC, the function returns <c>NULL</c>. Otherwise, the function
	/// returns the address of the function table entry that corresponds to the specified PC.
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtllookupfunctionentry NTSYSAPI PRUNTIME_FUNCTION
	// RtlLookupFunctionEntry( DWORD64 ControlPc, PDWORD64 ImageBase, PUNWIND_HISTORY_TABLE HistoryTable );
	[DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)]
	[PInvokeData("rtlsupportapi.h", MSDNShortId = "624b97fb-0453-4f47-b6bd-92aa14705e78")]
	public static extern IntPtr RtlLookupFunctionEntry(ulong ControlPc, out ulong ImageBase, out UNWIND_HISTORY_TABLE HistoryTable);

	/// <summary>Retrieves the base address of the image that contains the specified PC value.</summary>
	/// <param name="PcValue">
	/// The PC value. The function searches all modules mapped into the address space of the calling process for a module that contains
	/// this value.
	/// </param>
	/// <param name="BaseOfImage">
	/// The base address of the image containing the PC value. This value must be added to any relative addresses in the headers to
	/// locate the image.
	/// </param>
	/// <returns>
	/// <para>If the PC value is found, the function returns the base address of the image that contains the PC value.</para>
	/// <para>If no image contains the PC value, the function returns <c>NULL</c>.</para>
	/// </returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtlpctofileheader NTSYSAPI PVOID RtlPcToFileHeader( PVOID
	// PcValue, PVOID *BaseOfImage );
	[DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)]
	[PInvokeData("rtlsupportapi.h", MSDNShortId = "690c9f20-d471-49c9-a40c-28926f03acac")]
	public static extern IntPtr RtlPcToFileHeader(IntPtr PcValue, out IntPtr BaseOfImage);

	/// <summary>Restores the context of the caller to the specified context record.</summary>
	/// <param name="ContextRecord">A pointer to a CONTEXT structure.</param>
	/// <param name="ExceptionRecord">
	/// <para>A pointer to an EXCEPTION_RECORD structure. This parameter is optional and should typically be <c>NULL</c>.</para>
	/// <para>
	/// An exception record is used primarily with long jump and C++ catch-throw support. If the <c>ExceptionCode</c> member is
	/// STATUS_LONGJUMP, the <c>ExceptionInformation</c> member contains a pointer to a jump buffer. <c>RtlRestoreContext</c> will copy
	/// the non-volatile state from the jump buffer in to the context record before the context record is restored.
	/// </para>
	/// <para>
	/// If the <c>ExceptionCode</c> member is STATUS_UNWIND_CONSOLIDATE, the <c>ExceptionInformation</c> member contains a pointer to a
	/// callback function, such as a catch handler. <c>RtlRestoreContext</c> consolidates the call frames between its frame and the frame
	/// specified in the context record before calling the callback function. This hides frames from any exception handling that might
	/// occur in the callback function. The difference between this and a typical unwind is that the data on the stack is still present,
	/// so frame data such as a throw object is still available. The callback function returns a new program counter to update in the
	/// context record, which is then used in a normal restore context.
	/// </para>
	/// </param>
	/// <returns>This function does not return a value.</returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtlrestorecontext NTSYSAPI VOID RtlRestoreContext( PCONTEXT
	// ContextRecord, _EXCEPTION_RECORD *ExceptionRecord );
	[DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)]
	[PInvokeData("rtlsupportapi.h", MSDNShortId = "f5304d17-bc67-4e0f-a535-efca4e65c74c")]
	public static extern void RtlRestoreContext(in CONTEXT ContextRecord, in EXCEPTION_RECORD ExceptionRecord);

	/// <summary>Restores the context of the caller to the specified context record.</summary>
	/// <param name="ContextRecord">A pointer to a CONTEXT structure.</param>
	/// <param name="ExceptionRecord">
	/// <para>A pointer to an EXCEPTION_RECORD structure. This parameter is optional and should typically be <c>NULL</c>.</para>
	/// <para>
	/// An exception record is used primarily with long jump and C++ catch-throw support. If the <c>ExceptionCode</c> member is
	/// STATUS_LONGJUMP, the <c>ExceptionInformation</c> member contains a pointer to a jump buffer. <c>RtlRestoreContext</c> will copy
	/// the non-volatile state from the jump buffer in to the context record before the context record is restored.
	/// </para>
	/// <para>
	/// If the <c>ExceptionCode</c> member is STATUS_UNWIND_CONSOLIDATE, the <c>ExceptionInformation</c> member contains a pointer to a
	/// callback function, such as a catch handler. <c>RtlRestoreContext</c> consolidates the call frames between its frame and the frame
	/// specified in the context record before calling the callback function. This hides frames from any exception handling that might
	/// occur in the callback function. The difference between this and a typical unwind is that the data on the stack is still present,
	/// so frame data such as a throw object is still available. The callback function returns a new program counter to update in the
	/// context record, which is then used in a normal restore context.
	/// </para>
	/// </param>
	/// <returns>This function does not return a value.</returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtlrestorecontext NTSYSAPI VOID RtlRestoreContext( PCONTEXT
	// ContextRecord, _EXCEPTION_RECORD *ExceptionRecord );
	[DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)]
	[PInvokeData("rtlsupportapi.h", MSDNShortId = "f5304d17-bc67-4e0f-a535-efca4e65c74c")]
	public static extern void RtlRestoreContext(in CONTEXT ContextRecord, [In, Optional] IntPtr ExceptionRecord);

	/// <summary>Initiates an unwind of procedure call frames.</summary>
	/// <param name="TargetFrame">
	/// A pointer to the call frame that is the target of the unwind. If this parameter is <c>NULL</c>, the function performs an exit unwind.
	/// </param>
	/// <param name="TargetIp">The continuation address of the unwind. This parameter is ignored if TargetFrame is <c>NULL</c>.</param>
	/// <param name="ExceptionRecord">A pointer to an EXCEPTION_RECORD structure.</param>
	/// <param name="ReturnValue">A value to be placed in the integer function return register before continuing execution.</param>
	/// <returns>This function does not return a value.</returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtlunwind NTSYSAPI VOID RtlUnwind( PVOID TargetFrame, PVOID
	// TargetIp, PEXCEPTION_RECORD ExceptionRecord, PVOID ReturnValue );
	[DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)]
	[PInvokeData("rtlsupportapi.h", MSDNShortId = "254b2547-9d3d-468f-a360-20a12e9dd82e")]
	public static extern void RtlUnwind([In, Optional] IntPtr TargetFrame, [In, Optional] IntPtr TargetIp, in EXCEPTION_RECORD ExceptionRecord, IntPtr ReturnValue);

	/// <summary>Initiates an unwind of procedure call frames.</summary>
	/// <param name="TargetFrame">
	/// A pointer to the call frame that is the target of the unwind. If this parameter is <c>NULL</c>, the function performs an exit unwind.
	/// </param>
	/// <param name="TargetIp">The continuation address of the unwind. This parameter is ignored if TargetFrame is <c>NULL</c>.</param>
	/// <param name="ExceptionRecord">A pointer to an EXCEPTION_RECORD structure.</param>
	/// <param name="ReturnValue">A value to be placed in the integer function return register before continuing execution.</param>
	/// <returns>This function does not return a value.</returns>
	// https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtlunwind NTSYSAPI VOID RtlUnwind( PVOID TargetFrame, PVOID
	// TargetIp, PEXCEPTION_RECORD ExceptionRecord, PVOID ReturnValue );
	[DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)]
	[PInvokeData("rtlsupportapi.h", MSDNShortId = "254b2547-9d3d-468f-a360-20a12e9dd82e")]
	public static extern void RtlUnwind([In, Optional] IntPtr TargetFrame, [In, Optional] IntPtr TargetIp, [In, Optional] IntPtr ExceptionRecord, IntPtr ReturnValue);

	/// <summary>Initiates an unwind of procedure call frames.</summary>
	/// <param name="TargetFrame">
	/// A pointer to the call frame that is the target of the unwind. If this parameter is <c>NULL</c>, the function performs an exit unwind.
	/// </param>
	/// <param name="TargetIp">The continuation address of the unwind. This parameter is ignored if TargetFrame is <c>NULL</c>.</param>
	/// <param name="ExceptionRecord">A pointer to an EXCEPTION_RECORD structure.</param>
	/// <param name="ReturnValue">A value to be placed in the integer function return register before continuing execution.</param>
	/// <param name="ContextRecord">A pointer to a CONTEXT structure that stores context during the unwind operation.</param>
	/// <param name="HistoryTable">
	/// A pointer to the unwind history table. This structure is processor specific. For definitions of this structure, see Winternl.h.
	/// </param>
	/// <returns>This function does not return a value.</returns>
	/// <remarks>The <c>FRAME_POINTERS</c> structure is defined as follows:</remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtlunwindex NTSYSAPI VOID RtlUnwindEx( PVOID TargetFrame, PVOID
	// TargetIp, PEXCEPTION_RECORD ExceptionRecord, PVOID ReturnValue, PCONTEXT ContextRecord, PUNWIND_HISTORY_TABLE HistoryTable );
	[DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)]
	[PInvokeData("rtlsupportapi.h", MSDNShortId = "3d2d8778-311e-4cc1-b280-4f83ab457755")]
	public static extern void RtlUnwindEx([In, Optional] IntPtr TargetFrame, [In, Optional] IntPtr TargetIp, in EXCEPTION_RECORD ExceptionRecord, IntPtr ReturnValue, in CONTEXT ContextRecord, in UNWIND_HISTORY_TABLE HistoryTable);

	/// <summary>Initiates an unwind of procedure call frames.</summary>
	/// <param name="TargetFrame">
	/// A pointer to the call frame that is the target of the unwind. If this parameter is <c>NULL</c>, the function performs an exit unwind.
	/// </param>
	/// <param name="TargetIp">The continuation address of the unwind. This parameter is ignored if TargetFrame is <c>NULL</c>.</param>
	/// <param name="ExceptionRecord">A pointer to an EXCEPTION_RECORD structure.</param>
	/// <param name="ReturnValue">A value to be placed in the integer function return register before continuing execution.</param>
	/// <param name="ContextRecord">A pointer to a CONTEXT structure that stores context during the unwind operation.</param>
	/// <param name="HistoryTable">
	/// A pointer to the unwind history table. This structure is processor specific. For definitions of this structure, see Winternl.h.
	/// </param>
	/// <returns>This function does not return a value.</returns>
	/// <remarks>The <c>FRAME_POINTERS</c> structure is defined as follows:</remarks>
	// https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtlunwindex NTSYSAPI VOID RtlUnwindEx( PVOID TargetFrame, PVOID
	// TargetIp, PEXCEPTION_RECORD ExceptionRecord, PVOID ReturnValue, PCONTEXT ContextRecord, PUNWIND_HISTORY_TABLE HistoryTable );
	[DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)]
	[PInvokeData("rtlsupportapi.h", MSDNShortId = "3d2d8778-311e-4cc1-b280-4f83ab457755")]
	public static extern void RtlUnwindEx([In, Optional] IntPtr TargetFrame, [In, Optional] IntPtr TargetIp, [In, Optional] IntPtr ExceptionRecord, IntPtr ReturnValue, in CONTEXT ContextRecord, [In, Optional] IntPtr HistoryTable);
}